﻿using System;
using System.Globalization;
using System.IO;
using UnityEngine;
#if UNITY_2018_4_OR_NEWER && !NATIVE_GALLERY_DISABLE_ASYNC_FUNCTIONS
using System.Threading.Tasks;
using Unity.Collections;
using UnityEngine.Networking;
#endif
#if UNITY_ANDROID || UNITY_IOS
using NativeGalleryNamespace;
#endif
using Object = UnityEngine.Object;

public static class NativeGallery
{
    public struct ImageProperties
    {
        public readonly int width;
        public readonly int height;
        public readonly string mimeType;
        public readonly ImageOrientation orientation;

        public ImageProperties(int width, int height, string mimeType, ImageOrientation orientation)
        {
            this.width = width;
            this.height = height;
            this.mimeType = mimeType;
            this.orientation = orientation;
        }
    }

    public struct VideoProperties
    {
        public readonly int width;
        public readonly int height;
        public readonly long duration;
        public readonly float rotation;

        public VideoProperties(int width, int height, long duration, float rotation)
        {
            this.width = width;
            this.height = height;
            this.duration = duration;
            this.rotation = rotation;
        }
    }

    public enum PermissionType
    {
        Read = 0,
        Write = 1
    };

    public enum Permission
    {
        Denied = 0,
        Granted = 1,
        ShouldAsk = 2
    };

    [Flags]
    public enum MediaType
    {
        Image = 1,
        Video = 2,
        Audio = 4
    };

    // EXIF orientation: http://sylvana.net/jpegcrop/exif_orientation.html (indices are reordered)
    public enum ImageOrientation
    {
        Unknown = -1,
        Normal = 0,
        Rotate90 = 1,
        Rotate180 = 2,
        Rotate270 = 3,
        FlipHorizontal = 4,
        Transpose = 5,
        FlipVertical = 6,
        Transverse = 7
    };

    public delegate void PermissionCallback(Permission permission);

    public delegate void MediaSaveCallback(bool success, string path);

    public delegate void MediaPickCallback(string path);

	public delegate void GetVideoPropertiesCallback(VideoProperties properties);

	public delegate void GetImagePropertiesCallback(ImageProperties properties);

	public delegate void LoadImageAtPathCallback(Texture2D texture2D);

    public delegate void MediaPickMultipleCallback(string[] paths);

    public static Action<string> MediaPickOHOSResultCallBack = null;

    private static void MediaPickOHOSCallback(params OpenHarmonyJSObject[] args)
    {
        if (args.Length > 0)
        {
            Debug.Log("MediaPickOHOSCallback data: " + args[0].As<string>());
            MediaPickOHOSResultCallBack?.Invoke(args[0].As<string>());
        }
    }

    #region Platform Specific Elements

#if !UNITY_EDITOR && UNITY_ANDROID
	private static AndroidJavaClass m_ajc = null;
	private static AndroidJavaClass AJC
	{
		get
		{
			if( m_ajc == null )
				m_ajc = new AndroidJavaClass( "com.yasirkula.unity.NativeGallery" );

			return m_ajc;
		}
	}

	private static AndroidJavaObject m_context = null;
	private static AndroidJavaObject Context
	{
		get
		{
			if( m_context == null )
			{
				using( AndroidJavaObject unityClass = new AndroidJavaClass( "com.unity3d.player.UnityPlayer" ) )
				{
					m_context = unityClass.GetStatic<AndroidJavaObject>( "currentActivity" );
				}
			}

			return m_context;
		}
	}

#elif !UNITY_EDITOR && UNITY_OPENHARMONY
	// OpenHarmonyJSClass jsClass = new OpenHarmonyJSClass("NGSDKManager");

	private static OpenHarmonyJSObject oh_object = null;

	private static OpenHarmonyJSObject OH_Object {
		get{
			if(oh_object == null){
				oh_object = new OpenHarmonyJSObject("NGSDKManager");
			}
			return oh_object;
		}
	}

#elif !UNITY_EDITOR && UNITY_IOS
	[System.Runtime.InteropServices.DllImport( "__Internal" )]
	private static extern int _NativeGallery_CheckPermission( int readPermission, int permissionFreeMode );

	[System.Runtime.InteropServices.DllImport( "__Internal" )]
	private static extern int _NativeGallery_RequestPermission( int readPermission, int permissionFreeMode, int asyncMode );

	[System.Runtime.InteropServices.DllImport( "__Internal" )]
	private static extern void _NativeGallery_ShowLimitedLibraryPicker();

	[System.Runtime.InteropServices.DllImport( "__Internal" )]
	private static extern int _NativeGallery_CanOpenSettings();

	[System.Runtime.InteropServices.DllImport( "__Internal" )]
	private static extern void _NativeGallery_OpenSettings();

	[System.Runtime.InteropServices.DllImport( "__Internal" )]
	private static extern int _NativeGallery_CanPickMultipleMedia();

	[System.Runtime.InteropServices.DllImport( "__Internal" )]
	private static extern int _NativeGallery_GetMediaTypeFromExtension( string extension );

	[System.Runtime.InteropServices.DllImport( "__Internal" )]
	private static extern void _NativeGallery_ImageWriteToAlbum( string path, string album, int permissionFreeMode );

	[System.Runtime.InteropServices.DllImport( "__Internal" )]
	private static extern void _NativeGallery_VideoWriteToAlbum( string path, string album, int permissionFreeMode );

	[System.Runtime.InteropServices.DllImport( "__Internal" )]
	private static extern void _NativeGallery_PickMedia( string mediaSavePath, int mediaType, int permissionFreeMode, int selectionLimit );

	[System.Runtime.InteropServices.DllImport( "__Internal" )]
	private static extern string _NativeGallery_GetImageProperties( string path );

	[System.Runtime.InteropServices.DllImport( "__Internal" )]
	private static extern string _NativeGallery_GetVideoProperties( string path );

	[System.Runtime.InteropServices.DllImport( "__Internal" )]
	private static extern string _NativeGallery_GetVideoThumbnail( string path, string thumbnailSavePath, int maxSize, double captureTimeInSeconds );

	[System.Runtime.InteropServices.DllImport( "__Internal" )]
	private static extern string _NativeGallery_LoadImageAtPath( string path, string temporaryFilePath, int maxSize );
#endif

#if !UNITY_EDITOR && ( UNITY_ANDROID || UNITY_IOS || UNITY_OPENHARMONY)
	private static string m_temporaryImagePath = null;
	private static string TemporaryImagePath
	{
		get
		{
			if( m_temporaryImagePath == null )
			{
				m_temporaryImagePath = Path.Combine( Application.temporaryCachePath, "tmpImg" );
				Directory.CreateDirectory( Application.temporaryCachePath );
			}

			return m_temporaryImagePath;
		}
	}

	private static string m_selectedMediaPath = null;
	private static string SelectedMediaPath
	{
		get
		{
			if( m_selectedMediaPath == null )
			{
				m_selectedMediaPath = Path.Combine( Application.temporaryCachePath, "pickedMedia" );
				Directory.CreateDirectory( Application.temporaryCachePath );
			}

			return m_selectedMediaPath;
		}
	}
#endif

    #endregion

    #region Runtime Permissions

    // PermissionFreeMode was initially planned to be a toggleable setting on iOS but it has its own issues when set to false, so its value is forced to true.
    // These issues are:
    // - Presented permission dialog will have a "Select Photos" option on iOS 14+ but clicking it will freeze and eventually crash the app (I'm guessing that
    //   this is caused by how permissions are handled synchronously in NativeGallery)
    // - While saving images/videos to Photos, iOS 14+ users would see the "Select Photos" option (which is irrelevant in this context, hence confusing) and
    //   the user must grant full Photos access in order to save the image/video to a custom album
    // The only downside of having PermissionFreeMode = true is that, on iOS 14+, images/videos will be saved to the default Photos album rather than the
    // provided custom album
    private const bool PermissionFreeMode = true;

    public static Permission CheckPermission(PermissionType permissionType, MediaType mediaTypes)
    {
#if !UNITY_EDITOR && UNITY_ANDROID
		Permission result =
 (Permission) AJC.CallStatic<int>( "CheckPermission", Context, permissionType == PermissionType.Read, (int) mediaTypes );
		if( result == Permission.Denied && (Permission) PlayerPrefs.GetInt( "NativeGalleryPermission", (int) Permission.ShouldAsk ) == Permission.ShouldAsk )
			result = Permission.ShouldAsk;

		return result;
#elif !UNITY_EDITOR && UNITY_IOS
		return ProcessPermission( (Permission) _NativeGallery_CheckPermission( permissionType == PermissionType.Read ? 1 : 0, PermissionFreeMode ? 1 : 0 ) );
#elif (!UNITY_EDITOR && UNITY_OPENHARMONY)
		//CheckPermission alaways Granted
        return Permission.Granted;
#else
        return Permission.Granted;
#endif
    }

    public static Permission RequestPermission(PermissionType permissionType, MediaType mediaTypes)
    {
        // Don't block the main thread if the permission is already granted
        if (CheckPermission(permissionType, mediaTypes) == Permission.Granted)
            return Permission.Granted;

#if !UNITY_EDITOR && UNITY_ANDROID
		object threadLock = new object();
		lock( threadLock )
		{
			NGPermissionCallbackAndroid nativeCallback = new NGPermissionCallbackAndroid( threadLock );

			AJC.CallStatic( "RequestPermission", Context, nativeCallback, permissionType == PermissionType.Read, (int) mediaTypes, (int) Permission.ShouldAsk );

			if( nativeCallback.Result == -1 )
				System.Threading.Monitor.Wait( threadLock );

			if( (Permission) nativeCallback.Result != Permission.ShouldAsk && PlayerPrefs.GetInt( "NativeGalleryPermission", -1 ) != nativeCallback.Result )
			{
				PlayerPrefs.SetInt( "NativeGalleryPermission", nativeCallback.Result );
				PlayerPrefs.Save();
			}

			return (Permission) nativeCallback.Result;
		}
#elif !UNITY_EDITOR && UNITY_IOS
		return ProcessPermission( (Permission) _NativeGallery_RequestPermission( permissionType == PermissionType.Read ? 1 : 0, PermissionFreeMode ? 1 : 0, 0 ) );

#elif (!UNITY_EDITOR && UNITY_OPENHARMONY)
	Debug.Log( "OHOS: RequestPermission" );
	// Debug.LogError( "OHOS RequestPermission not suppport RequestPermission, please use RequestPermissionAsync method" );
    // RequestPermission alaways Granted
	return Permission.Granted;
#else
    return Permission.Granted;
#endif
    }

    public static void RequestPermissionAsync(PermissionCallback callback, PermissionType permissionType,
        MediaType mediaTypes)
    {
#if !UNITY_EDITOR && UNITY_ANDROID
		NGPermissionCallbackAsyncAndroid nativeCallback = new NGPermissionCallbackAsyncAndroid( callback );
		AJC.CallStatic( "RequestPermission", Context, nativeCallback, permissionType == PermissionType.Read, (int) mediaTypes, (int) Permission.ShouldAsk );
#elif !UNITY_EDITOR && UNITY_IOS
		NGPermissionCallbackiOS.Initialize( ( result ) => callback( ProcessPermission( result ) ) );
		_NativeGallery_RequestPermission( permissionType == PermissionType.Read ? 1 : 0, PermissionFreeMode ? 1 : 0, 1 );

#elif (!UNITY_EDITOR && UNITY_OPENHARMONY)
	Debug.Log( "OHOS: RequestPermissionAsync" );
    // RequestPermissionAsync alaways Granted
    callback(Permission.Granted);
#else
        callback(Permission.Granted);
#endif
    }

#if UNITY_2018_4_OR_NEWER && !NATIVE_GALLERY_DISABLE_ASYNC_FUNCTIONS
    public static Task<Permission> RequestPermissionAsync(PermissionType permissionType, MediaType mediaTypes)
    {
        TaskCompletionSource<Permission> tcs = new TaskCompletionSource<Permission>();
        RequestPermissionAsync((permission) => tcs.SetResult(permission), permissionType, mediaTypes);
        return tcs.Task;
    }
#endif

    private static Permission ProcessPermission(Permission permission)
    {
        // result == 3: LimitedAccess permission on iOS, no need to handle it when PermissionFreeMode is set to true
        return (PermissionFreeMode && (int)permission == 3) ? Permission.Granted : permission;
    }

    // This function isn't needed when PermissionFreeMode is set to true
    private static void TryExtendLimitedAccessPermission()
    {
        if (IsMediaPickerBusy())
            return;

#if !UNITY_EDITOR && UNITY_IOS
		_NativeGallery_ShowLimitedLibraryPicker();
#endif
    }

    public static bool CanOpenSettings()
    {
#if !UNITY_EDITOR && UNITY_IOS
		return _NativeGallery_CanOpenSettings() == 1;
#else
        return true;
#endif
    }

    public static void OpenSettings()
    {
#if !UNITY_EDITOR && UNITY_ANDROID
		AJC.CallStatic( "OpenSettings", Context );
#elif !UNITY_EDITOR && UNITY_IOS
		_NativeGallery_OpenSettings();
#elif (!UNITY_EDITOR && UNITY_OPENHARMONY)
		Debug.Log( "OHOS: OpenSettings" );


		OpenHarmonyJSCallback callback = new OpenHarmonyJSCallback((OpenHarmonyJSObject[] args)=>{
					Debug.Log("OpenSettings OpenHarmonyJSCallback: " + args.Length);
			if (args.Length > 0)
			{
				Debug.Log("OpenSettings  Type: " + args[0].As<string>());
				Debug.Log("OpenSettings  Status: " + args[1].As<string>());
				Debug.Log("OpenSettings  Data: " + args[2].As<string>());
			}
		});

		OH_Object.Call( "OpenSettings", callback);
#endif
    }

    #endregion

    #region Save Functions

    public static Permission SaveImageToGallery(byte[] mediaBytes, string album, string filename,
        MediaSaveCallback callback = null)
    {
        return SaveToGallery(mediaBytes, album, filename, MediaType.Image, callback);
    }

    public static Permission SaveImageToGallery(string existingMediaPath, string album, string filename,
        MediaSaveCallback callback = null)
    {
        return SaveToGallery(existingMediaPath, album, filename, MediaType.Image, callback);
    }

    public static Permission SaveImageToGallery(Texture2D image, string album, string filename,
        MediaSaveCallback callback = null)
    {
        if (image == null)
            throw new ArgumentException("Parameter 'image' is null!");

        if (filename.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) ||
            filename.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase))
            return SaveToGallery(GetTextureBytes(image, true), album, filename, MediaType.Image, callback);
        else if (filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
            return SaveToGallery(GetTextureBytes(image, false), album, filename, MediaType.Image, callback);
        else
            return SaveToGallery(GetTextureBytes(image, false), album, filename + ".png", MediaType.Image, callback);
    }

    public static Permission SaveVideoToGallery(byte[] mediaBytes, string album, string filename,
        MediaSaveCallback callback = null)
    {
        return SaveToGallery(mediaBytes, album, filename, MediaType.Video, callback);
    }

    public static Permission SaveVideoToGallery(string existingMediaPath, string album, string filename,
        MediaSaveCallback callback = null)
    {
        return SaveToGallery(existingMediaPath, album, filename, MediaType.Video, callback);
    }

    private static Permission SaveAudioToGallery(byte[] mediaBytes, string album, string filename,
        MediaSaveCallback callback = null)
    {
        return SaveToGallery(mediaBytes, album, filename, MediaType.Audio, callback);
    }

    private static Permission SaveAudioToGallery(string existingMediaPath, string album, string filename,
        MediaSaveCallback callback = null)
    {
        return SaveToGallery(existingMediaPath, album, filename, MediaType.Audio, callback);
    }

    #endregion

    #region Load Functions

    public static bool CanSelectMultipleFilesFromGallery()
    {
#if !UNITY_EDITOR && UNITY_ANDROID
		return AJC.CallStatic<bool>( "CanSelectMultipleMedia" );
#elif !UNITY_EDITOR && UNITY_IOS
		return _NativeGallery_CanPickMultipleMedia() == 1;
#elif !UNITY_EDITOR && UNITY_OPENHARMONY
		//默认可以多选
		return OH_Object.Call<bool>("CanSelectMultipleFilesFromGallery");
#else
        return false;
#endif
    }

    public static bool CanSelectMultipleMediaTypesFromGallery()
    {
#if UNITY_EDITOR
        return true;
#elif UNITY_ANDROID
		return AJC.CallStatic<bool>( "CanSelectMultipleMediaTypes" );
#elif UNITY_IOS
		return true;
#elif UNITY_OPENHARMONY
		// 默认可多选 
		return OH_Object.Call<bool>("CanSelectMultipleMediaTypesFromGallery");
#else
		return false;
#endif
    }

    public static Permission GetImageFromGallery(MediaPickCallback callback, string title = "", string mime = "image/*")
    {
        return GetMediaFromGallery(callback, MediaType.Image, mime, title);
    }

    public static Permission GetVideoFromGallery(MediaPickCallback callback, string title = "", string mime = "video/*")
    {
        return GetMediaFromGallery(callback, MediaType.Video, mime, title);
    }

    public static Permission GetAudioFromGallery(MediaPickCallback callback, string title = "", string mime = "audio/*")
    {
	#if UNITY_OPENHARMONY
		Debug.LogError("OHOS: not support get audio file in gallery.");
        return Permission.Denied;
	#else
        return GetMediaFromGallery(callback, MediaType.Audio, mime, title);
	#endif
    }

    public static Permission GetMixedMediaFromGallery(MediaPickCallback callback, MediaType mediaTypes,
        string title = "")
    {
        return GetMediaFromGallery(callback, mediaTypes, "*/*", title);
    }

    public static Permission GetImagesFromGallery(MediaPickMultipleCallback callback, string title = "",
        string mime = "image/*")
    {
        return GetMultipleMediaFromGallery(callback, MediaType.Image, mime, title);
    }

    public static Permission GetVideosFromGallery(MediaPickMultipleCallback callback, string title = "",
        string mime = "video/*")
    {
        return GetMultipleMediaFromGallery(callback, MediaType.Video, mime, title);
    }

    public static Permission GetAudiosFromGallery(MediaPickMultipleCallback callback, string title = "",
        string mime = "audio/*")
    {
	#if UNITY_OPENHARMONY
		Debug.LogError("OHOS: not support get audio file in gallery.");
        return Permission.Denied;
	#else
        return GetMultipleMediaFromGallery(callback, MediaType.Audio, mime, title);
	#endif
    }

    public static Permission GetMixedMediasFromGallery(MediaPickMultipleCallback callback, MediaType mediaTypes,
        string title = "")
    {
        return GetMultipleMediaFromGallery(callback, mediaTypes, "*/*", title);
    }

    public static bool IsMediaPickerBusy()
    {
#if !UNITY_EDITOR && UNITY_IOS
		return NGMediaReceiveCallbackiOS.IsBusy;
#else
        return false;
#endif
    }

    public static MediaType GetMediaTypeOfFile(string path)
    {
        if (string.IsNullOrEmpty(path))
            return (MediaType)0;

        string extension = Path.GetExtension(path);
        if (string.IsNullOrEmpty(extension))
            return (MediaType)0;

        if (extension[0] == '.')
        {
            if (extension.Length == 1)
                return (MediaType)0;

            extension = extension.Substring(1);
        }

#if UNITY_EDITOR || UNITY_OPENHARMONY
        extension = extension.ToLowerInvariant();
        if (extension == "png" || extension == "jpg" || extension == "jpeg" || extension == "gif" ||
            extension == "bmp" || extension == "tiff")
            return MediaType.Image;
        else if (extension == "mp4" || extension == "mov" || extension == "wav" || extension == "avi")
            return MediaType.Video;
        else if (extension == "mp3" || extension == "aac" || extension == "flac")
            return MediaType.Audio;

        return (MediaType)0;
#elif UNITY_ANDROID
		string mime = AJC.CallStatic<string>( "GetMimeTypeFromExtension", extension.ToLowerInvariant() );
		if( string.IsNullOrEmpty( mime ) )
			return (MediaType) 0;
		else if( mime.StartsWith( "image/" ) )
			return MediaType.Image;
		else if( mime.StartsWith( "video/" ) )
			return MediaType.Video;
		else if( mime.StartsWith( "audio/" ) )
			return MediaType.Audio;
		else
			return (MediaType) 0;
#elif UNITY_IOS
		return (MediaType) _NativeGallery_GetMediaTypeFromExtension( extension.ToLowerInvariant() );
#else
		return (MediaType) 0;
#endif
    }

    #endregion

    #region Internal Functions

    private static Permission SaveToGallery(byte[] mediaBytes, string album, string filename, MediaType mediaType,
        MediaSaveCallback callback)
    {
// todo
#if UNITY_OPENHARMONY
	Permission result = CheckPermission(PermissionType.Write, mediaType);
#else 
	Permission result = RequestPermission(PermissionType.Write, mediaType);
#endif
        if (result == Permission.Granted)
        {
            if (mediaBytes == null || mediaBytes.Length == 0)
                throw new ArgumentException("Parameter 'mediaBytes' is null or empty!");

            if (album == null || album.Length == 0)
                throw new ArgumentException("Parameter 'album' is null or empty!");

            if (filename == null || filename.Length == 0)
                throw new ArgumentException("Parameter 'filename' is null or empty!");

            if (string.IsNullOrEmpty(Path.GetExtension(filename)))
                Debug.LogWarning("'filename' doesn't have an extension, this might result in unexpected behaviour!");

            string path = GetTemporarySavePath(filename);
#if UNITY_EDITOR
            Debug.Log("SaveToGallery called successfully in the Editor");
#else
			File.WriteAllBytes( path, mediaBytes );
#endif

            SaveToGalleryInternal(path, album, mediaType, callback);
        }else{
			Debug.LogWarning("SaveToGallery permission denied! please call RequestPermission or RequestPermissionAsync get permission first.");
		}
        return result;
    }

    private static Permission SaveToGallery(string existingMediaPath, string album, string filename,
        MediaType mediaType, MediaSaveCallback callback)
    {
		// todo
#if UNITY_OPENHARMONY
	Permission result = CheckPermission(PermissionType.Write, mediaType);
#else 
	Permission result = RequestPermission(PermissionType.Write, mediaType);
#endif
        if (result == Permission.Granted)
        {
            if (!File.Exists(existingMediaPath))
                throw new FileNotFoundException("File not found at " + existingMediaPath);

            if (album == null || album.Length == 0)
                throw new ArgumentException("Parameter 'album' is null or empty!");

            if (filename == null || filename.Length == 0)
                throw new ArgumentException("Parameter 'filename' is null or empty!");

            if (string.IsNullOrEmpty(Path.GetExtension(filename)))
            {
                string originalExtension = Path.GetExtension(existingMediaPath);
                if (string.IsNullOrEmpty(originalExtension))
                    Debug.LogWarning(
                        "'filename' doesn't have an extension, this might result in unexpected behaviour!");
                else
                    filename += originalExtension;
            }

            string path = GetTemporarySavePath(filename);
#if UNITY_EDITOR
            Debug.Log("SaveToGallery called successfully in the Editor");
#else
			File.Copy( existingMediaPath, path, true );
#endif

            SaveToGalleryInternal(path, album, mediaType, callback);
        }else{
			Debug.LogWarning("SaveToGallery permission denied! please call RequestPermission or RequestPermissionAsync get permission first.");
		}

        return result;
    }

    public static void SaveToGalleryInternal(string path, string album, MediaType mediaType, MediaSaveCallback callback)
    {
#if !UNITY_EDITOR && UNITY_ANDROID
		string savePath = AJC.CallStatic<string>( "SaveMedia", Context, (int) mediaType, path, album );

		File.Delete( path );

		if( callback != null )
			callback( !string.IsNullOrEmpty( savePath ), savePath );
#elif !UNITY_EDITOR && UNITY_IOS
		if( mediaType == MediaType.Audio )
		{
			Debug.LogError( "Saving audio files is not supported on iOS" );

			if( callback != null )
				callback( false, null );

			return;
		}

		Debug.Log( "Saving to Pictures: " + Path.GetFileName( path ) );

		NGMediaSaveCallbackiOS.Initialize( callback );
		if( mediaType == MediaType.Image )
			_NativeGallery_ImageWriteToAlbum( path, album, PermissionFreeMode ? 1 : 0 );
		else if( mediaType == MediaType.Video )
			_NativeGallery_VideoWriteToAlbum( path, album, PermissionFreeMode ? 1 : 0 );

#elif (!UNITY_EDITOR && UNITY_OPENHARMONY)
	Debug.Log( "OHOS: SaveToGalleryInternal" );

	OpenHarmonyJSCallback OH_callback = new OpenHarmonyJSCallback((OpenHarmonyJSObject[] args)=>{
		Debug.Log("SaveToGalleryInternal: " + args.Length);
		if (args.Length > 0)
		{
			Debug.Log("SaveToGalleryInternal  Type: " + args[0].As<string>());
			Debug.Log("SaveToGalleryInternal  Status: " + args[1].As<string>());
			Debug.Log("SaveToGalleryInternal  Data: " + args[2].As<string>());

			string savePath = args[2].As<string>();
			if(callback!= null){
				callback(!string.IsNullOrEmpty( savePath ), savePath);
			}
		}
	});
	OH_Object.Call("SaveMedia", OH_callback, (int)mediaType, path, album );

#else
        if (callback != null)
            callback(true, null);
#endif
    }

    private static string GetTemporarySavePath(string filename)
    {
        string saveDir = Path.Combine(Application.persistentDataPath, "NGallery");
        Directory.CreateDirectory(saveDir);

#if !UNITY_EDITOR && UNITY_IOS
		// Ensure a unique temporary filename on iOS:
		// iOS internally copies images/videos to Photos directory of the system,
		// but the process is async. The redundant file is deleted by objective-c code
		// automatically after the media is saved but while it is being saved, the file
		// should NOT be overwritten. Therefore, always ensure a unique filename on iOS
		string path = Path.Combine( saveDir, filename );
		if( File.Exists( path ) )
		{
			int fileIndex = 0;
			string filenameWithoutExtension = Path.GetFileNameWithoutExtension( filename );
			string extension = Path.GetExtension( filename );

			do
			{
				path = Path.Combine( saveDir, string.Concat( filenameWithoutExtension, ++fileIndex, extension ) );
			} while( File.Exists( path ) );
		}

		return path;
#else
        return Path.Combine(saveDir, filename);
#endif
    }

    public static Permission GetMediaFromGallery(MediaPickCallback callback, MediaType mediaType, string mime,
        string title)
    {
#if UNITY_OPENHARMONY
		Permission result = CheckPermission(PermissionType.Write, mediaType);
#else 
		Permission result = RequestPermission(PermissionType.Write, mediaType);
#endif
		if (result == Permission.Granted && !IsMediaPickerBusy())
        {
#if UNITY_EDITOR
            System.Collections.Generic.List<string> editorFilters = new System.Collections.Generic.List<string>(4);

            if ((mediaType & MediaType.Image) == MediaType.Image)
            {
                editorFilters.Add("Image files");
                editorFilters.Add("png,jpg,jpeg");
            }

            if ((mediaType & MediaType.Video) == MediaType.Video)
            {
                editorFilters.Add("Video files");
                editorFilters.Add("mp4,mov,webm,avi");
            }

            if ((mediaType & MediaType.Audio) == MediaType.Audio)
            {
                editorFilters.Add("Audio files");
                editorFilters.Add("mp3,wav,aac,flac");
            }

            editorFilters.Add("All files");
            editorFilters.Add("*");

            string pickedFile =
                UnityEditor.EditorUtility.OpenFilePanelWithFilters("Select file", "", editorFilters.ToArray());

            if (callback != null)
                callback(pickedFile != "" ? pickedFile : null);
#elif UNITY_ANDROID
			AJC.CallStatic( "PickMedia", Context, new NGMediaReceiveCallbackAndroid( callback, null ), (int) mediaType, false, SelectedMediaPath, mime, title );

#elif UNITY_OPENHARMONY
	Debug.Log( "OHOS: GetMediaFromGallery" );
	OpenHarmonyJSCallback OH_callback = new OpenHarmonyJSCallback((OpenHarmonyJSObject[] args)=>{
		Debug.Log("GetMediaFromGallery OpenHarmonyJSCallback: " + args.Length);
		if (args.Length > 0)
		{
			Debug.Log("GetMediaFromGallery  Type: " + args[0].As<string>());
			Debug.Log("GetMediaFromGallery  Status: " + args[1].As<string>());
			Debug.Log("GetMediaFromGallery  Data: " + args[2].As<string>());

            string result = args[2].As<string>();
            string[] pickedFiles = new string[]{};
            if(!string.IsNullOrEmpty(result)){
                pickedFiles = result.Substring(1, result.Length - 2).Split(",");
            }

			if(callback != null){
				if(pickedFiles.Length > 0){
					callback(pickedFiles[0]);
				}else{
					callback(null);
				}
			}
		}
	});

	OH_Object.Call("PickMedia", OH_callback, (int) mediaType, false, mime);

#elif UNITY_IOS
			if( mediaType == MediaType.Audio )
			{
				Debug.LogError( "Picking audio files is not supported on iOS" );

				if( callback != null ) // Selecting audio files is not supported on iOS
					callback( null );
			}
			else
			{
				NGMediaReceiveCallbackiOS.Initialize( callback, null );
				_NativeGallery_PickMedia( SelectedMediaPath, (int) ( mediaType & ~MediaType.Audio ), PermissionFreeMode ? 1 : 0, 1 );
			}
#else
			if( callback != null )
				callback( null );
#endif
        }else{
			Debug.LogWarning("SaveToGallery permission denied! please call RequestPermission or RequestPermissionAsync get permission first.");
		}
        return result;
    }

    private static Permission GetMultipleMediaFromGallery(MediaPickMultipleCallback callback, MediaType mediaType,
        string mime, string title)
    {
#if UNITY_OPENHARMONY
		Permission result = CheckPermission(PermissionType.Write, mediaType);
#else 
		Permission result = RequestPermission(PermissionType.Write, mediaType);
#endif
        if (result == Permission.Granted && !IsMediaPickerBusy())
        {
            if (CanSelectMultipleFilesFromGallery())
            {
#if !UNITY_EDITOR && UNITY_ANDROID
				AJC.CallStatic( "PickMedia", Context, new NGMediaReceiveCallbackAndroid( null, callback ), (int) mediaType, true, SelectedMediaPath, mime, title );

#elif !UNITY_EDITOR && UNITY_OPENHARMONY
			Debug.Log( "OHOS: GetMultipleMediaFromGallery" );

			OpenHarmonyJSCallback OH_callback = new OpenHarmonyJSCallback((OpenHarmonyJSObject[] args)=>{
				Debug.Log("GetMultipleMediaFromGallery OpenHarmonyJSCallback: " + args.Length);
				if (args.Length > 0)
				{
					Debug.Log("GetMultipleMediaFromGallery  Type: " + args[0].As<string>());
					Debug.Log("GetMultipleMediaFromGallery  Status: " + args[1].As<string>());
					Debug.Log("GetMultipleMediaFromGallery  Data: " + args[2].As<string>());

					string result = args[2].As<string>();
                    string[] pickedFiles = new string[]{};
                    if(!string.IsNullOrEmpty(result)){
                        pickedFiles = result.Substring(1, result.Length - 2).Split(",");
                    }

					if(callback != null){
						if(pickedFiles.Length > 0){
							callback(pickedFiles);
						}else{
							callback(null);
						}
					}
				}
			});

			OH_Object.Call("PickMedia", OH_callback, (int) mediaType, true, SelectedMediaPath, mime, title);

#elif !UNITY_EDITOR && UNITY_IOS
				if( mediaType == MediaType.Audio )
				{
					Debug.LogError( "Picking audio files is not supported on iOS" );

					if( callback != null ) // Selecting audio files is not supported on iOS
						callback( null );
				}
				else
				{
					NGMediaReceiveCallbackiOS.Initialize( null, callback );
					_NativeGallery_PickMedia( SelectedMediaPath, (int) ( mediaType & ~MediaType.Audio ), PermissionFreeMode ? 1 : 0, 0 );
				}
#else
                if (callback != null)
                    callback(null);
#endif
            }
            else if (callback != null)
                callback(null);
        }else{
			Debug.LogWarning("SaveToGallery permission denied! please call RequestPermission or RequestPermissionAsync get permission first.");
		}

        return result;
    }

    private static byte[] GetTextureBytes(Texture2D texture, bool isJpeg)
    {
        try
        {
            return isJpeg ? texture.EncodeToJPG(100) : texture.EncodeToPNG();
        }
        catch (UnityException)
        {
            return GetTextureBytesFromCopy(texture, isJpeg);
        }
        catch (ArgumentException)
        {
            return GetTextureBytesFromCopy(texture, isJpeg);
        }

#pragma warning disable 0162
        return null;
#pragma warning restore 0162
    }

    private static byte[] GetTextureBytesFromCopy(Texture2D texture, bool isJpeg)
    {
        // Texture is marked as non-readable, create a readable copy and save it instead
        Debug.LogWarning("Saving non-readable textures is slower than saving readable textures");

        Texture2D sourceTexReadable = null;
        RenderTexture rt = RenderTexture.GetTemporary(texture.width, texture.height);
        RenderTexture activeRT = RenderTexture.active;

        try
        {
            Graphics.Blit(texture, rt);
            RenderTexture.active = rt;

            sourceTexReadable = new Texture2D(texture.width, texture.height,
                isJpeg ? TextureFormat.RGB24 : TextureFormat.RGBA32, false);
            sourceTexReadable.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0, false);
            sourceTexReadable.Apply(false, false);
        }
        catch (Exception e)
        {
            Debug.LogException(e);

            Object.DestroyImmediate(sourceTexReadable);
            return null;
        }
        finally
        {
            RenderTexture.active = activeRT;
            RenderTexture.ReleaseTemporary(rt);
        }

        try
        {
            return isJpeg ? sourceTexReadable.EncodeToJPG(100) : sourceTexReadable.EncodeToPNG();
        }
        catch (Exception e)
        {
            Debug.LogException(e);
            return null;
        }
        finally
        {
            Object.DestroyImmediate(sourceTexReadable);
        }
    }

#if UNITY_2018_4_OR_NEWER && !NATIVE_GALLERY_DISABLE_ASYNC_FUNCTIONS
    private static async Task<T> TryCallNativeAndroidFunctionOnSeparateThread<T>(Func<T> function)
    {
        T result = default(T);
        bool hasResult = false;

        await Task.Run(() =>
        {
            if (AndroidJNI.AttachCurrentThread() != 0)
                Debug.LogWarning("Couldn't attach JNI thread, calling native function on the main thread");
            else
            {
                try
                {
                    result = function();
                    hasResult = true;
                }
                finally
                {
                    AndroidJNI.DetachCurrentThread();
                }
            }
        });

        return hasResult ? result : function();
    }
#endif

    #endregion

    #region Utility Functions

	public static void LoadImageAtPathOHOS(LoadImageAtPathCallback callback, string imagePath, int maxSize = -1, bool markTextureNonReadable = true,
        bool generateMipmaps = true, bool linearColorSpace = false){
#if !UNITY_EDITOR && UNITY_OPENHARMONY
        Debug.Log( "OHOS: LoadImageAtPathOHOS" );
		if (string.IsNullOrEmpty(imagePath))
            throw new ArgumentException("Parameter 'imagePath' is null or empty!");
		
		if (maxSize <= 0)
            maxSize = SystemInfo.maxTextureSize;

        string extension = Path.GetExtension(imagePath).ToLowerInvariant();
        string tempPath = Path.Combine( Application.temporaryCachePath, "tmpImg"+extension );
        Debug.Log("LoadImageAtPathOHOS tempPath:" + tempPath);

        Directory.CreateDirectory( Application.temporaryCachePath );
		OpenHarmonyJSCallback OH_callback = new OpenHarmonyJSCallback(async (OpenHarmonyJSObject[] args)=>{
			Debug.Log("LoadImageAtPathOHOS OpenHarmonyJSCallback: " + args.Length);
			if (args.Length > 0)
			{
				Debug.Log("LoadImageAtPathOHOS  Type: " + args[0].As<string>());
				Debug.Log("LoadImageAtPathOHOS  Status: " + args[1].As<string>());
				Debug.Log("LoadImageAtPathOHOS  Data: " + args[2].As<string>());

				string loadPath = args[2].As<string>();
				if(!string.IsNullOrEmpty(loadPath)){
                    TextureFormat format =(extension == ".jpg" || extension == ".jpeg") ? TextureFormat.RGB24 : TextureFormat.RGBA32;
                    Texture2D Texture2D =await GetTexture2D(tempPath, format, generateMipmaps, linearColorSpace);
                    callback(Texture2D);
				}else{
					Debug.Log("File not found at "+ imagePath);
					callback(null);
				}
			}
		});
        
		OH_Object.Call<string>( "LoadImageAtPath", OH_callback, imagePath, tempPath, maxSize);
#endif
	}

    public static async Task<Texture2D> GetTexture2D(string tempPath, TextureFormat format, bool generateMipmaps = true, bool linearColorSpace = false){
        Texture2D result = new Texture2D(2, 2, format, generateMipmaps, linearColorSpace);
        // string tempPath = Path.Combine( Application.temporaryCachePath, "tmpImg"+extension );
        // Debug.Log("LoadImageAtPathOHOS tempPath:" + tempPath);
        using (UnityWebRequest req = UnityWebRequestTexture.GetTexture("file://"+tempPath)){
            UnityWebRequestAsyncOperation asyncOperation = req.SendWebRequest();
            // req.SendWebRequest();
            while(!asyncOperation.isDone){
                Debug.Log("LoadImageAtPathOHOS not done ");
                await Task.Yield();
            }
            Debug.Log("LoadImageAtPathOHOS SendWebRequest is done");

            if(req.result == UnityWebRequest.Result.Success){
                Debug.Log("LoadImageAtPathOHOS req.result Success");
                result = DownloadHandlerTexture.GetContent(req);
            }else{
                Debug.Log("LoadImageAtPathOHOS req.result failure");
            }
        }
        return result;
    }

    public static Texture2D LoadImageAtPath(string imagePath, int maxSize = -1, bool markTextureNonReadable = true,
        bool generateMipmaps = true, bool linearColorSpace = false)
    {

#if UNITY_OPENHARMONY
		Debug.Log( "OHOS: LoadImageAtPathAsync" );
		Debug.LogError("OHOS: LoadImageAtPathAsync not support, please call LoadImageAtPathOHOS.");
		return null;
#else
        if (string.IsNullOrEmpty(imagePath))
            throw new ArgumentException("Parameter 'imagePath' is null or empty!");

        if (!File.Exists(imagePath))
            throw new FileNotFoundException("File not found at " + imagePath);

        if (maxSize <= 0)
            maxSize = SystemInfo.maxTextureSize;

#if !UNITY_EDITOR && UNITY_ANDROID
		string loadPath = AJC.CallStatic<string>( "LoadImageAtPath", Context, imagePath, TemporaryImagePath, maxSize );

#elif !UNITY_EDITOR && UNITY_IOS
		string loadPath = _NativeGallery_LoadImageAtPath( imagePath, TemporaryImagePath, maxSize );
#else
        string loadPath = imagePath;
#endif

        string extension = Path.GetExtension(imagePath).ToLowerInvariant();
        TextureFormat format =
            (extension == ".jpg" || extension == ".jpeg") ? TextureFormat.RGB24 : TextureFormat.RGBA32;

        Texture2D result = new Texture2D(2, 2, format, generateMipmaps, linearColorSpace);

        try
        {
            if (!result.LoadImage(File.ReadAllBytes(loadPath), markTextureNonReadable))
            {
                Debug.LogWarning("Couldn't load image at path: " + loadPath);

                Object.DestroyImmediate(result);
                return null;
            }
        }
        catch (Exception e)
        {
            Debug.LogException(e);

            Object.DestroyImmediate(result);
            return null;
        }
        finally
        {
            if (loadPath != imagePath)
            {
                try
                {
                    File.Delete(loadPath);
                }
                catch
                {
                }
            }
        }

        return result;
#endif
    }

#if UNITY_2018_4_OR_NEWER && !NATIVE_GALLERY_DISABLE_ASYNC_FUNCTIONS
    public static async Task<Texture2D> LoadImageAtPathAsync(string imagePath, int maxSize = -1,
        bool markTextureNonReadable = true)
    {
#if UNITY_OPENHARMONY
		Debug.Log( "OHOS: LoadImageAtPathAsync" );
		Debug.LogError("OHOS: LoadImageAtPathAsync not support, please call LoadImageAtPathOHOS.");
		return null;
#else
        if (string.IsNullOrEmpty(imagePath))
            throw new ArgumentException("Parameter 'imagePath' is null or empty!");
        if (!File.Exists(imagePath))
            throw new FileNotFoundException("File not found at " + imagePath);
        if (maxSize <= 0)
            maxSize = SystemInfo.maxTextureSize;

#if !UNITY_EDITOR && UNITY_ANDROID
		string temporaryImagePath = TemporaryImagePath; // Must be accessed from main thread
		string loadPath =
 await TryCallNativeAndroidFunctionOnSeparateThread( () => AJC.CallStatic<string>( "LoadImageAtPath", Context, imagePath, temporaryImagePath, maxSize ) );

#elif !UNITY_EDITOR && UNITY_IOS
		string temporaryImagePath = TemporaryImagePath; // Must be accessed from main thread
		string loadPath =
 await Task.Run( () => _NativeGallery_LoadImageAtPath( imagePath, temporaryImagePath, maxSize ) );
#else
        string loadPath = imagePath;
#endif

        Texture2D result = null;

        using (UnityWebRequest www = UnityWebRequestTexture.GetTexture("file://" + loadPath, markTextureNonReadable))
        {
            UnityWebRequestAsyncOperation asyncOperation = www.SendWebRequest();
            while (!asyncOperation.isDone)
                await Task.Yield();

#if UNITY_2020_1_OR_NEWER
            if (www.result != UnityWebRequest.Result.Success)
#else
			if( www.isNetworkError || www.isHttpError )
#endif
            {
                Debug.LogWarning("Couldn't use UnityWebRequest to load image, falling back to LoadImage: " + www.error);
            }
            else
                result = DownloadHandlerTexture.GetContent(www);
        }

        if (!result) // Fallback to Texture2D.LoadImage if something goes wrong
        {
            string extension = Path.GetExtension(imagePath).ToLowerInvariant();
            TextureFormat format = (extension == ".jpg" || extension == ".jpeg")
                ? TextureFormat.RGB24
                : TextureFormat.RGBA32;

            result = new Texture2D(2, 2, format, true, false);

            try
            {
                if (!result.LoadImage(File.ReadAllBytes(loadPath), markTextureNonReadable))
                {
                    Debug.LogWarning("Couldn't load image at path: " + loadPath);

                    Object.DestroyImmediate(result);
                    return null;
                }
            }
            catch (Exception e)
            {
                Debug.LogException(e);

                Object.DestroyImmediate(result);
                return null;
            }
            finally
            {
                if (loadPath != imagePath)
                {
                    try
                    {
                        File.Delete(loadPath);
                    }
                    catch
                    {
                    }
                }
            }
        }

        return result;
#endif
    }
#endif

    public static Texture2D GetVideoThumbnail(string videoPath, int maxSize = -1, double captureTimeInSeconds = -1.0,
        bool markTextureNonReadable = true, bool generateMipmaps = true, bool linearColorSpace = false)
    {
        if (maxSize <= 0)
            maxSize = SystemInfo.maxTextureSize;
        string thumbnailPath = null;
#if !UNITY_EDITOR && UNITY_ANDROID
		thumbnailPath =
 AJC.CallStatic<string>( "GetVideoThumbnail", Context, videoPath, TemporaryImagePath + ".png", false, maxSize, captureTimeInSeconds );
#elif !UNITY_EDITOR && UNITY_IOS
		thumbnailPath =
 _NativeGallery_GetVideoThumbnail( videoPath, TemporaryImagePath + ".png", maxSize, captureTimeInSeconds );
#elif !UNITY_EDITOR && UNITY_OPENHARMONY
		Debug.Log( "OHOS: GetVideoThumbnail" );
		Debug.LogError("OHOS: GetVideoThumbnail not support. return null");
		return null;
#endif

        if (!string.IsNullOrEmpty(thumbnailPath))
            return LoadImageAtPath(thumbnailPath, maxSize, markTextureNonReadable, generateMipmaps, linearColorSpace);
        else
            return null;
    }

#if UNITY_2018_4_OR_NEWER && !NATIVE_GALLERY_DISABLE_ASYNC_FUNCTIONS
    public static async Task<Texture2D> GetVideoThumbnailAsync(string videoPath, int maxSize = -1,
        double captureTimeInSeconds = -1.0, bool markTextureNonReadable = true)
    {
        if (maxSize <= 0)
            maxSize = SystemInfo.maxTextureSize;
        string thumbnailPath = null;
#if !UNITY_EDITOR && UNITY_ANDROID
		string temporaryImagePath = TemporaryImagePath; // Must be accessed from main thread
		thumbnailPath =
 await TryCallNativeAndroidFunctionOnSeparateThread( () => AJC.CallStatic<string>( "GetVideoThumbnail", Context, videoPath, temporaryImagePath + ".png", false, maxSize, captureTimeInSeconds ) );
#elif !UNITY_EDITOR && UNITY_OPENHARMONY
		Debug.Log( "OHOS: GetVideoThumbnailAsync" );
		Debug.LogError("OHOS: GetVideoThumbnailAsync not support. return null");
        return null;
#elif !UNITY_EDITOR && UNITY_IOS
		string temporaryImagePath = TemporaryImagePath; // Must be accessed from main thread
		thumbnailPath =
 await Task.Run( () => _NativeGallery_GetVideoThumbnail( videoPath, temporaryImagePath + ".png", maxSize, captureTimeInSeconds ) );
#endif

        if (!string.IsNullOrEmpty(thumbnailPath))
            return await LoadImageAtPathAsync(thumbnailPath, maxSize, markTextureNonReadable);
        else
            return null;
    }
#endif


    public static void GetImagePropertiesAsync(GetImagePropertiesCallback callback, String imagePath)
    {
#if !UNITY_EDITOR && UNITY_OPENHARMONY
        Debug.Log( "OHOS: GetImageProperties" );
        OpenHarmonyJSCallback OH_callback = new OpenHarmonyJSCallback((OpenHarmonyJSObject[] args)=>{
		Debug.Log("GetImagePropertiesAsync OpenHarmonyJSCallback: " + args.Length);
		if (args.Length > 0)
		{
			Debug.Log("GetImagePropertiesAsync  Type: " + args[0].As<string>());
			Debug.Log("GetImagePropertiesAsync  Status: " + args[1].As<string>());
			Debug.Log("GetImagePropertiesAsync  Data: " + args[2].As<string>());
			string propertiesStr = args[2].As<string>();

			string extension = Path.GetExtension(imagePath).ToLowerInvariant();
			ImageProperties properties = strFormateImageProperties(propertiesStr, extension);
			if(callback != null){
				callback(properties);
			}
		}
	});
        OH_Object.Call("GetImageProperties",OH_callback, imagePath);
#endif
    }


public static ImageProperties strFormateImageProperties(string value, string extension){
	int width = 0, height = 0;
	string mimeType = null;
	ImageOrientation orientation = ImageOrientation.Unknown;
	if (!string.IsNullOrEmpty(value))
	{
		string[] properties = value.Split('>');
		if (properties != null && properties.Length >= 4)
		{
			if (!int.TryParse(properties[0].Trim(), out width))
				width = 0;
			if (!int.TryParse(properties[1].Trim(), out height))
				height = 0;

			mimeType = properties[2].Trim();
			if (mimeType.Length == 0)
			{
				if (extension == ".png")
					mimeType = "image/png";
				else if (extension == ".jpg" || extension == ".jpeg")
					mimeType = "image/jpeg";
				else if (extension == ".gif")
					mimeType = "image/gif";
				else if (extension == ".bmp")
					mimeType = "image/bmp";
				else
					mimeType = null;
			}

			int orientationInt;
			if (int.TryParse(properties[3].Trim(), out orientationInt))
				orientation = (ImageOrientation)orientationInt;
		}
	}
	return new ImageProperties(width, height, mimeType, orientation);
}


    public static ImageProperties GetImageProperties(string imagePath)
    {
#if !UNITY_EDITOR && UNITY_OPENHARMONY
	Debug.LogError( "OHOS: GetImageProperties not support, please call GetImagePropertiesAsync." );
	return new ImageProperties(0, 0, null, ImageOrientation.Unknown);
#else
        if (!File.Exists(imagePath))
            throw new FileNotFoundException("File not found at " + imagePath);
#if !UNITY_EDITOR && UNITY_ANDROID
		string value = AJC.CallStatic<string>( "GetImageProperties", Context, imagePath );
#elif !UNITY_EDITOR && UNITY_IOS
		string value = _NativeGallery_GetImageProperties( imagePath );
#else
        string value = null;
#endif
		string extension = Path.GetExtension(imagePath).ToLowerInvariant();
        return strFormateImageProperties(value, extension);
#endif
    }


    public static void GetVideoPropertiesAsync(GetVideoPropertiesCallback callback, string videoPath)
    {
#if !UNITY_EDITOR && UNITY_OPENHARMONY
        Debug.Log( "OHOS: GetVideoPropertiesAsync" );
		OpenHarmonyJSCallback OH_callback = new OpenHarmonyJSCallback((OpenHarmonyJSObject[] args)=>{
		Debug.Log("GetVideoPropertiesAsync OpenHarmonyJSCallback: " + args.Length);
		if (args.Length > 0)
		{
			Debug.Log("GetVideoPropertiesAsync  Type: " + args[0].As<string>());
			Debug.Log("GetVideoPropertiesAsync  Status: " + args[1].As<string>());
			Debug.Log("GetVideoPropertiesAsync  Data: " + args[2].As<string>());

			string resultStr = args[2].As<string>();
			VideoProperties properties = strFormat(resultStr);
			if(callback != null){
				callback(properties);
			}
		}
	});
        OH_Object.Call("GetVideoProperties",OH_callback, videoPath);
#endif
    }


	public static VideoProperties strFormat(string propertiesStr){
		int width = 0, height = 0;
        long duration = 0L;
        float rotation = 0f;
        if (!string.IsNullOrEmpty(propertiesStr))
        {
            string[] properties = propertiesStr.Split('>');
            if (properties != null && properties.Length >= 4)
            {
                if (!int.TryParse(properties[0].Trim(), out width))
                    width = 0;
                if (!int.TryParse(properties[1].Trim(), out height))
                    height = 0;
                if (!long.TryParse(properties[2].Trim(), out duration))
                    duration = 0L;
                if (!float.TryParse(properties[3].Trim().Replace(',', '.'), NumberStyles.Float,
                        CultureInfo.InvariantCulture, out rotation))
                    rotation = 0f;
            }
        }

        if (rotation == -90f)
            rotation = 270f;

        return new VideoProperties(width, height, duration, rotation);
	}

    public static VideoProperties GetVideoProperties(string videoPath)
    {
#if !UNITY_EDITOR && UNITY_OPENHARMONY
	Debug.LogError( "OHOS: GetVideoProperties not support, please call GetVideoPropertiesAsync." );
	return new VideoProperties(0, 0, 0L, 0f);
#else
        if (!File.Exists(videoPath))
            throw new FileNotFoundException("File not found at " + videoPath);

#if !UNITY_EDITOR && UNITY_ANDROID
		string value = AJC.CallStatic<string>( "GetVideoProperties", Context, videoPath );
		
#elif !UNITY_EDITOR && UNITY_IOS
		string value = _NativeGallery_GetVideoProperties( videoPath );
#else
        string value = null;
#endif

	return strFormat(value);
#endif
    }

    #endregion
}